前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# 二进制数据 buffer

30 秒速记

  • Buffer = 一段固定长度的原始字节,Node.js 处理文件、网络、加密数据都靠它
  • 本质是 Uint8Array 的子类,每个元素是 0~255 的整数
  • 内存在 V8 堆外分配,所以大文件读进来不太占 JS 堆,但会占进程的 RSS
  • 全局可用,推荐显式 require('node:buffer') 写清依赖
  • 和字符串互转一定要说清编码:utf8、base64、hex、latin1

Buffer 就是 Node.js 里装原始字节的容器,读文件、收网络包、做加解密,拿到的底层数据都是它。 浏览器里的 JS 早期只有字符串,没有办法直接摆弄二进制,Node.js 就自己造了 Buffer;后来 ES6 有了 TypedArray,Buffer 也改成了 Uint8Array 的子类,所以能用下标读写每个字节、能用 for...of 遍历。它的长度在创建时就定死了,不能 push。和字符串互相转换时一定要带上编码,否则默认 utf8,中文、base64 之类场景最容易在这里出乱码。

理解「二进制数据 buffer」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 buffer 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Buffer 可以理解成一段「不可变长度、可修改内容」的字节数组,它解决的是 JS 早期没有二进制类型的问题。几个关键事实:

  • 从 Node 4 起,Buffer 是 Uint8Array 的子类,buf[0]、buf.length、buf.subarray() 这些 TypedArray 的能力都能用。
  • 字节内容在 V8 堆外分配。process.memoryUsage() 里看 arrayBuffers 和 external,不在 heapUsed。
  • 字符串和 Buffer 之间的转换必须指定编码,默认 utf8。
const buf = Buffer.from('hi中', 'utf8');
console.log(buf);              // <Buffer 68 69 e4 b8 ad>
console.log(buf.length);       // 5,字节数
console.log(buf.toString('hex'));    // 6869e4b8ad
console.log(buf.toString('base64')); // aGnkuK0=
console.log(buf instanceof Uint8Array); // true
@前端进阶之旅: 代码已经复制到剪贴板

日常最常见的用法是:读文件、处理 HTTP 请求体、base64 编解码、计算哈希。只要涉及「字节」而不是「字符」,就该想到它。

面试官追问

追问 1Buffer.from('中').length 是多少?
参考回答

3。utf8 下一个常见汉字占 3 个字节,length 是字节数不是字符数,算上传大小、协议长度时要用 Buffer.byteLength(str)。

追问 2一个 Buffer 能不能当成 Uint8Array 传给只接受 Uint8Array 的库?
参考回答

能,Buffer 就是它的子类。反过来把 Uint8Array 转成 Buffer 用 Buffer.from(u8.buffer, u8.byteOffset, u8.byteLength),不拷贝内存。

追问 3fs.readFile 读一个 500MB 的文件,heapUsed 没怎么涨,进程内存却很高,为什么?
参考回答

Buffer 的数据在 V8 堆外,算在 external / arrayBuffers 和 RSS 里,不在 heapUsed。大文件别一次性读,用 fs.createReadStream 分块处理。

追问 4一个 utf8 字符被切在两个 chunk 中间,chunk.toString() 会乱码,怎么办?
参考回答

用 string_decoder 的 StringDecoder,它会把不完整的字节留到下一块再拼;或者给流设 setEncoding('utf8'),内部就是用它处理的。

# 模块概览

30 秒速记

  • Buffer 是 Node.js 核心模块,专门处理二进制:文件读写、网络数据、加解密、图片处理
  • 常用 API 按用途分组记:创建、比较、拼接、拷贝、查找、遍历、转换、截取、编码
  • 创建:Buffer.from / alloc / allocUnsafe;拼接:Buffer.concat;比较:equals / compare
  • 截取用 subarray()(slice() 已不推荐),两者都和原 Buffer 共享内存,改一个另一个也变
  • 和字符串转换:toString(encoding) / Buffer.from(str, encoding)

Buffer 的 API 很多,但按「创建、读写、拼接比较、转换」几组去记就够用了。 创建优先 Buffer.from 和 Buffer.alloc;收网络数据时一块块拿到的 chunk 用 Buffer.concat 拼起来;比较两个 Buffer 内容用 equals,排序用 Buffer.compare;转字符串用 toString 并指定编码。最容易踩坑的是截取:subarray() 和 slice() 都不拷贝内存,返回的是原数据的一个视图,改了截出来的部分原 Buffer 也跟着变,要独立副本就用 Buffer.from(sub) 再拷一份。

Buffer是node的核心模块,开发者可以利用它来处理二进制数据,比如文件流的读写、网络请求数据的处理等。

Buffer的API非常多,本文仅挑选 比较常用/容易理解 的API进行讲解,包括Buffer实例的创建、比较、连接、拷贝、查找、遍历、类型转换、截取、编码转换等。

面试官追问

追问 1const b = buf.slice(0, 2); b[0] = 0;,原来的 buf 会变吗?
参考回答

会。Buffer 的 slice() 和 subarray() 一样共享内存,不像数组的 slice 是拷贝。要独立的就写 Buffer.from(buf.subarray(0, 2))。

追问 2HTTP 请求体分成多个 chunk 到达,怎么拼成完整数据?
参考回答

chunk 先 push 进数组,end 时 Buffer.concat(chunks) 一次拼好,再统一 toString('utf8')。不要每个 chunk 直接 += 成字符串,多字节字符会被切坏。

追问 3比较两个 Buffer 内容是否一样,能用 === 吗?
参考回答

不能,=== 比的是引用。用 a.equals(b);比较密钥、签名这种安全敏感数据要用 crypto.timingSafeEqual(a, b),防时序攻击。

追问 4Buffer 和 base64 字符串怎么互转?
参考回答

buf.toString('base64') 编码,Buffer.from(str, 'base64') 解码;URL 里用的话换成 'base64url' 编码(Node 15.7+),不会出现 + 和 /。

# 创建

30 秒速记

  • new Buffer(...) 已废弃(DEP0005),Node 10 起在非 node_modules 代码里调用会打运行时警告
  • Buffer.alloc(n):分配 n 字节并填 0,安全
  • Buffer.allocUnsafe(n):不清零,快,但里面可能是旧内存数据,必须马上写满
  • Buffer.from(array | string | buffer | arrayBuffer):按内容创建;传 arrayBuffer 时共享内存,传 buffer 时拷贝
  • allocUnsafe 小于 4KB 的走内部 8KB 内存池,所以更快

创建 Buffer 现在只用三个工厂方法:Buffer.from 按已有内容创建,Buffer.alloc 要一块干净的空内存,Buffer.allocUnsafe 要一块不清零的快速内存。 new Buffer() 被废弃是因为它的行为随参数类型变化太大:传字符串是按内容创建,传数字是按长度分配,而且 Node 8 之前传数字拿到的内存不清零,里面可能有别的请求留下的数据,如果参数来自用户输入就会泄露内存内容。拆成三个方法以后,意图清楚,安全和性能也能自己选。我的默认选择是 from 和 alloc,只有确定马上会把整块写满时才用 allocUnsafe。

  • new Buffer(array)
  • Buffer.alloc(length)
  • Buffer.allocUnsafe(length)
  • Buffer.from(array)

创建 Buffer 的几种方式对照着看最清楚:

const a = Buffer.alloc(4);          // <Buffer 00 00 00 00>,清零
const b = Buffer.allocUnsafe(4);    // 内容不确定,可能是旧数据
const c = Buffer.from([1, 2, 3]);   // <Buffer 01 02 03>
const d = Buffer.from('abc');       // <Buffer 61 62 63>,默认 utf8
const e = Buffer.from('YWJj', 'base64'
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
      • 模块概览
      • 创建
        • 通过 new Buffer(array)
        • 通过 Buffer.alloc(length)
        • 通过Buffer.from()
      • buffer比较
      • 从Buffer.from([62])谈起
      • Buffer.from(‘1’)
      • buffer连接:Buffer.concat(list[, totalLength])
      • 拷贝:buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
      • 查找:buf.indexOf(value[, byteOffset][, encoding])
      • 写:buf.write(string[, offset[, length]][, encoding])
      • 填充:buf.fill(value[, offset[, end]][, encoding])
      • 转成字符串: buf.toString([encoding[, start[, end]]])
      • 转成JSON字符串:buf.toJSON()
      • 遍历:buf.values()、buf.keys()、buf.entries()
      • 截取:buf.slice([start[, end]])
      • TODO
      • 文档摘要
      • 相关链接
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

二进制数据 buffer|Nodejs专题系列

# 二进制数据 buffer

30 秒速记

  • Buffer = 一段固定长度的原始字节,Node.js 处理文件、网络、加密数据都靠它
  • 本质是 Uint8Array 的子类,每个元素是 0~255 的整数
  • 内存在 V8 堆外分配,所以大文件读进来不太占 JS 堆,但会占进程的 RSS
  • 全局可用,推荐显式 require('node:buffer') 写清依赖
  • 和字符串互转一定要说清编码:utf8、base64、hex、latin1

Buffer 就是 Node.js 里装原始字节的容器,读文件、收网络包、做加解密,拿到的底层数据都是它。 浏览器里的 JS 早期只有字符串,没有办法直接摆弄二进制,Node.js 就自己造了 Buffer;后来 ES6 有了 TypedArray,Buffer 也改成了 Uint8Array 的子类,所以能用下标读写每个字节、能用 for...of 遍历。它的长度在创建时就定死了,不能 push。和字符串互相转换时一定要带上编码,否则默认 utf8,中文、base64 之类场景最容易在这里出乱码。

理解「二进制数据 buffer」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 buffer 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Buffer 可以理解成一段「不可变长度、可修改内容」的字节数组,它解决的是 JS 早期没有二进制类型的问题。几个关键事实:

  • 从 Node 4 起,Buffer 是 Uint8Array 的子类,buf[0]、buf.length、buf.subarray() 这些 TypedArray 的能力都能用。
  • 字节内容在 V8 堆外分配。process.memoryUsage() 里看 arrayBuffers 和 external,不在 heapUsed。
  • 字符串和 Buffer 之间的转换必须指定编码,默认 utf8。
const buf = Buffer.from('hi中', 'utf8');
console.log(buf);              // <Buffer 68 69 e4 b8 ad>
console.log(buf.length);       // 5,字节数
console.log(buf.toString('hex'));    // 6869e4b8ad
console.log(buf.toString('base64')); // aGnkuK0=
console.log(buf instanceof Uint8Array); // true
@前端进阶之旅: 代码已经复制到剪贴板

日常最常见的用法是:读文件、处理 HTTP 请求体、base64 编解码、计算哈希。只要涉及「字节」而不是「字符」,就该想到它。

面试官追问

追问 1Buffer.from('中').length 是多少?
参考回答

3。utf8 下一个常见汉字占 3 个字节,length 是字节数不是字符数,算上传大小、协议长度时要用 Buffer.byteLength(str)。

追问 2一个 Buffer 能不能当成 Uint8Array 传给只接受 Uint8Array 的库?
参考回答

能,Buffer 就是它的子类。反过来把 Uint8Array 转成 Buffer 用 Buffer.from(u8.buffer, u8.byteOffset, u8.byteLength),不拷贝内存。

追问 3fs.readFile 读一个 500MB 的文件,heapUsed 没怎么涨,进程内存却很高,为什么?
参考回答

Buffer 的数据在 V8 堆外,算在 external / arrayBuffers 和 RSS 里,不在 heapUsed。大文件别一次性读,用 fs.createReadStream 分块处理。

追问 4一个 utf8 字符被切在两个 chunk 中间,chunk.toString() 会乱码,怎么办?
参考回答

用 string_decoder 的 StringDecoder,它会把不完整的字节留到下一块再拼;或者给流设 setEncoding('utf8'),内部就是用它处理的。

# 模块概览

30 秒速记

  • Buffer 是 Node.js 核心模块,专门处理二进制:文件读写、网络数据、加解密、图片处理
  • 常用 API 按用途分组记:创建、比较、拼接、拷贝、查找、遍历、转换、截取、编码
  • 创建:Buffer.from / alloc / allocUnsafe;拼接:Buffer.concat;比较:equals / compare
  • 截取用 subarray()(slice() 已不推荐),两者都和原 Buffer 共享内存,改一个另一个也变
  • 和字符串转换:toString(encoding) / Buffer.from(str, encoding)

Buffer 的 API 很多,但按「创建、读写、拼接比较、转换」几组去记就够用了。 创建优先 Buffer.from 和 Buffer.alloc;收网络数据时一块块拿到的 chunk 用 Buffer.concat 拼起来;比较两个 Buffer 内容用 equals,排序用 Buffer.compare;转字符串用 toString 并指定编码。最容易踩坑的是截取:subarray() 和 slice() 都不拷贝内存,返回的是原数据的一个视图,改了截出来的部分原 Buffer 也跟着变,要独立副本就用 Buffer.from(sub) 再拷一份。

Buffer是node的核心模块,开发者可以利用它来处理二进制数据,比如文件流的读写、网络请求数据的处理等。

Buffer的API非常多,本文仅挑选 比较常用/容易理解 的API进行讲解,包括Buffer实例的创建、比较、连接、拷贝、查找、遍历、类型转换、截取、编码转换等。

面试官追问

追问 1const b = buf.slice(0, 2); b[0] = 0;,原来的 buf 会变吗?
参考回答

会。Buffer 的 slice() 和 subarray() 一样共享内存,不像数组的 slice 是拷贝。要独立的就写 Buffer.from(buf.subarray(0, 2))。

追问 2HTTP 请求体分成多个 chunk 到达,怎么拼成完整数据?
参考回答

chunk 先 push 进数组,end 时 Buffer.concat(chunks) 一次拼好,再统一 toString('utf8')。不要每个 chunk 直接 += 成字符串,多字节字符会被切坏。

追问 3比较两个 Buffer 内容是否一样,能用 === 吗?
参考回答

不能,=== 比的是引用。用 a.equals(b);比较密钥、签名这种安全敏感数据要用 crypto.timingSafeEqual(a, b),防时序攻击。

追问 4Buffer 和 base64 字符串怎么互转?
参考回答

buf.toString('base64') 编码,Buffer.from(str, 'base64') 解码;URL 里用的话换成 'base64url' 编码(Node 15.7+),不会出现 + 和 /。

# 创建

30 秒速记

  • new Buffer(...) 已废弃(DEP0005),Node 10 起在非 node_modules 代码里调用会打运行时警告
  • Buffer.alloc(n):分配 n 字节并填 0,安全
  • Buffer.allocUnsafe(n):不清零,快,但里面可能是旧内存数据,必须马上写满
  • Buffer.from(array | string | buffer | arrayBuffer):按内容创建;传 arrayBuffer 时共享内存,传 buffer 时拷贝
  • allocUnsafe 小于 4KB 的走内部 8KB 内存池,所以更快

创建 Buffer 现在只用三个工厂方法:Buffer.from 按已有内容创建,Buffer.alloc 要一块干净的空内存,Buffer.allocUnsafe 要一块不清零的快速内存。 new Buffer() 被废弃是因为它的行为随参数类型变化太大:传字符串是按内容创建,传数字是按长度分配,而且 Node 8 之前传数字拿到的内存不清零,里面可能有别的请求留下的数据,如果参数来自用户输入就会泄露内存内容。拆成三个方法以后,意图清楚,安全和性能也能自己选。我的默认选择是 from 和 alloc,只有确定马上会把整块写满时才用 allocUnsafe。

  • new Buffer(array)
  • Buffer.alloc(length)
  • Buffer.allocUnsafe(length)
  • Buffer.from(array)

创建 Buffer 的几种方式对照着看最清楚:

const a = Buffer.alloc(4);          // <Buffer 00 00 00 00>,清零
const b = Buffer.allocUnsafe(4);    // 内容不确定,可能是旧数据
const c = Buffer.from([1, 2, 3]);   // <Buffer 01 02 03>
const d = Buffer.from('abc');       // <Buffer 61 62 63>,默认 utf8
const e = Buffer.from('YWJj', 'base64'); // <Buffer 61 62 63>

const ab = new ArrayBuffer(4);
const shared = Buffer.from(ab);     // 和 ab 共享内存
shared[0] = 1;
console.log(new Uint8Array(ab)[0]); // 1

const copy = Buffer.from(c);        // 拷贝
copy[0] = 9;
console.log(c[0]);                  // 1,原来的没变
@前端进阶之旅: 代码已经复制到剪贴板

new Buffer() 已经废弃(DEP0005),原因是同一个构造函数根据参数类型做完全不同的事,传数字时在 Node 8 之前还会拿到未清零的内存。现在的约定是:要按内容创建用 from,要空内存用 alloc,追求性能并且确定会立即写满才用 allocUnsafe。allocUnsafe 对小于 Buffer.poolSize >>> 1(默认 4KB)的请求会从一个 8KB 的共享池里切片,所以频繁创建小 Buffer 时更快。

← 7.2 子进程 child8.2 二进制解码 string_decoder →

fe